﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using System.Reflection;
using System.Globalization;
using System.Threading;
using System.Web;
using System.Web.Hosting;
using System.Web.Mvc;
using System.Web.Routing;
using System.Configuration;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Vit.Common;
using Vit.Common.Providers;
using Vit.Common.Providers.Caching;
using Vit.Common.Providers.Localization;
using Vit.Common.Providers.Logging;
using Vit.Web.Modules;
using Vit.Web.Mvc;
using Vit.Web.Mvc.Filters;
using Vit.Web.Providers;
using Vit.Web.Helpers;
using Vit.Web.Mvc.DataAnnotations;
using Vit.Web.Models;

namespace Vit.Web
{
    public abstract class UnityMvcApplication : System.Web.HttpApplication
    {
        protected virtual IUnityContainer SetupUnity()
        {
            IUnityContainer parentContainer = new UnityContainer();
            parentContainer
                .RegisterInstance(RouteTable.Routes)
                .RegisterInstance(ModelBinders.Binders)
                .RegisterInstance(HostingEnvironment.VirtualPathProvider);

            foreach (ConnectionStringSettings connectionString in ConfigurationManager.ConnectionStrings)
            {
                parentContainer.RegisterInstance(connectionString.Name, connectionString.ConnectionString);
            }

            parentContainer
                .RegisterType<IControllerFactory, DefaultControllerFactory>(new ContainerControlledLifetimeManager())
                .RegisterType<IControllerActivator, UnityControllerActivator>(new ContainerControlledLifetimeManager())
                .RegisterType<ModelMetadataProvider, AdditionalModelMetadataProvider>(new ContainerControlledLifetimeManager());

            ViewEngines.Engines.Clear();
            ViewEngines.Engines.Add(new ThemedCshtmlViewEngine());

            ModelBinders.Binders.DefaultBinder = new DataSourceFormModelBinder();

            CriteriaFilterProvider filterProvider = new CriteriaFilterProvider();
            FilterProviders.Providers.Add(filterProvider);

            // register common providers
            parentContainer
                .RegisterType<ICacheProvider, WebCacheProvider>(new ContainerControlledLifetimeManager())
                .RegisterType<ILocalizationProvider, JsonResourceProvider>(new ContainerControlledLifetimeManager());

            IUnityContainer childContainer = parentContainer.CreateChildContainer();
            UnityConfigurationSection config = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
            config.Configure(childContainer);
            childContainer.RegisterInstance(childContainer);

            UnityDependencyResolver resolver = new UnityDependencyResolver(childContainer);
            DependencyResolver.SetResolver(resolver);

            return childContainer;
        }

        protected virtual void Application_Start(object sender, EventArgs e)
        {
            IUnityContainer unity = SetupUnity();
            Unity.SetContainer(unity);

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterIgnoreRoutes(RouteTable.Routes);

            Type moduleType = typeof(IModule);
            foreach (Assembly asm in ModuleAssemblies)
            {
                string areaName = string.Empty;
                foreach (Type type in asm.GetTypes().Where(o => o.IsAbstract == false))
                {
                    // register areas
                    if (moduleType.IsAssignableFrom(type) && type.GetConstructor(Type.EmptyTypes) != null)
                    {
                        IModule module = Activator.CreateInstance(type) as IModule;
                        if (typeof(AreaModuleBase).IsAssignableFrom(type))
                        {
                            areaName = ((AreaModuleBase)module).AreaName;
                            RegisterAreaRegisterContext((AreaModuleBase)module, RouteTable.Routes);
                        }
                        unity.RegisterInstance<IModule>(module.Name.ToLower(), module);
                        module.OnApplicationStart(this, unity);
                    }
                }
            }

            RegisterMapRoutes(RouteTable.Routes);
        }

        protected virtual void Application_End(object sender, EventArgs e)
        {
            IEnumerable<ModuleBase> areas = DependencyResolver.Current.GetServices<ModuleBase>();
            foreach (var area in areas)
                area.OnApplicationEnd(sender as HttpApplication, Unity.Current);
        }

        protected virtual void Application_BeginRequest(object sender, EventArgs e)
        {
            IEnumerable<ModuleBase> areas = DependencyResolver.Current.GetServices<ModuleBase>();
            foreach (var area in areas)
                area.OnBeginRequest(sender as HttpApplication, Unity.Current);
        }

        protected virtual void Application_EndRequest(object sender, EventArgs e)
        {
            IEnumerable<ModuleBase> areas = DependencyResolver.Current.GetServices<ModuleBase>();
            foreach (var area in areas)
                area.OnEndRequest(sender as HttpApplication, Unity.Current);
        }

        protected virtual void Session_Start(object sender, EventArgs e)
        {
            IEnumerable<ModuleBase> areas = DependencyResolver.Current.GetServices<ModuleBase>();
            foreach (var area in areas)
                area.OnSessionStart(sender as HttpApplication, Unity.Current);
        }

        protected virtual void Session_End(object sender, EventArgs e)
        {
            IEnumerable<ModuleBase> areas = DependencyResolver.Current.GetServices<ModuleBase>();
            foreach (var area in areas)
                area.OnSessionEnd(sender as HttpApplication, Unity.Current);
        }

        protected virtual void Application_PostAcquireRequestState(object sender, EventArgs e)
        {
            string cultureInfoName = PortalContext.Current.User.Profiles.GetProfileValue("CultureInfo", "zh-CN");
            CultureInfo cultureInfo = new CultureInfo(cultureInfoName);
            Thread.CurrentThread.CurrentCulture = cultureInfo;
            Thread.CurrentThread.CurrentUICulture = cultureInfo;
        }

        private void RegisterAreaRegisterContext(AreaRegistration area, RouteCollection routes)
        {
            AreaRegistrationContext context = new AreaRegistrationContext(area.AreaName, routes, null);
            string str = area.GetType().Namespace;
            if (str != null)
            {
                context.Namespaces.Add(str + ".*");
            }
            area.RegisterArea(context);
        }

        protected virtual void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new HandleErrorAttribute(), 1);
            //filters.Add(new ExceptionFilter(), 2);
            filters.Add(new AntiForgeryAuthorizationFilter(), 3);
            filters.Add(new PermissionAuthorizationFilter(), 4);
            filters.Add(new PageViewFilter(), 5);
        }

        protected virtual void RegisterIgnoreRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
            routes.IgnoreRoute("{resource}.ico/{*pathInfo}");

            routes.IgnoreRoute("{*Images}", new { Images = @"(.*/)?Images(/.*)?" });
            routes.IgnoreRoute("{*Styles}", new { Styles = @"(.*/)?Styles(/.*)?" });
            routes.IgnoreRoute("{*Scripts}", new { Scripts = @"(.*/)?Scripts(/.*)?" });
        }

        protected virtual void RegisterMapRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                "Default", // Route name
                "{controller}/{action}/{id}", // URL with parameters
                new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
                new string[] { "Vit.Web.Mvc.Controllers", "Vit.Site.Controllers" }
            );
        }

        protected virtual IEnumerable<Assembly> ModuleAssemblies
        {
            get
            {
                return PortalHelper.Assemblies;
            }
        }
    }
}
